package com.web_bloks.content.client;

import java.util.HashMap;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
//import com.google.gwt.user.client.History;
import com.google.gwt.user.client.HistoryListener;
import com.google.gwt.user.client.Window;

import com.web_bloks.document.client.NamespaceException;
import com.web_bloks.document.client.TitleException;
import com.web_bloks.system.client.Logger;
import com.web_bloks.url.client.Helper;
import com.web_bloks.url.client.UrlFields;
import com.web_bloks.utilities.client.AnchorsUtil;
import com.web_bloks.utilities.client.stringUtil;
import com.web_bloks.document.client.Title;

/**
 * 
 * @author Jean-Lou Dupont
 *
 */
public class PortalManager implements HistoryListener {

	/**
	 * PortalManager's Response class
	 */
	public class Response {
		ResponseCode code = ResponseCode.NA;
		String page = null;
		String view = null;
		
		public Response() {
			super();
		}
	}
	/**
	 * PortalManager's response code class 
	 */
	public enum ResponseCode {
		NA, OK, INVALID_PAGE, SAME_PAGE,
	}
	
	/**
	 * NavigateCommand: uses deferred command
	 */
	protected class NavigateCommand implements Command {
		PortalManager pm = null;
		UrlFields target = null;
		
		public NavigateCommand(PortalManager pm, String historyToken) {
			super();
			this.pm = pm;
			this.target = Helper.extractFieldsFromHistoryToken(historyToken);
		}
		
		public NavigateCommand(PortalManager pm, UrlFields target) {
			super();			
			this.pm = pm;
			this.target = target;
		}
		public void execute() {
			pm.navigateTo( target );
		}
	}

	//***********************************
	static Response  invalidPageResponse = null;
	static Response  samePageResponse = null;
	static UrlFields defaultPage = null;
	
	MessageManager		messageManager		= null;
	BasePage 			basePage 			= null;
	FlowManager			flowManager			= null;
	UserMessageManager	userMessageManager	= null;
	
	/*****************************************
	 * CONSTRUCTOR
	 *****************************************/
	public PortalManager() {
		super();
		setup();
	}
	private void setup() {
		
		if (null==invalidPageResponse) {
			Response r =  new PortalManager.Response();
			r.code = ResponseCode.INVALID_PAGE;
			
			invalidPageResponse = r;
		}
		if (null==samePageResponse) {
			Response r = new PortalManager.Response();
			r.code = ResponseCode.SAME_PAGE;
			
			samePageResponse = r;
		}
	
	}
	
	/*****************************************
	 * CONFIGURATION
	 *****************************************/
	public void setBasePage(BasePage bp) {
		this.basePage = bp;
	}
	public void setFlowManager(FlowManager fm) {
		this.flowManager = fm;
	}
	public void setUserMessageManager(UserMessageManager umm) {
		this.userMessageManager = umm;
	}
	public void setMessageManager(MessageManager mm) {
		this.messageManager = mm;
	}
	/*****************************************
	 * API
	 *****************************************/
	public Response navigateTo(Title title) {
		if (null == title)
			throw new RuntimeException(this.getClass()+".navigateTo: title can't NULL");
		
		Logger.logDebug(this.getClass()+".navigateTo: BEGIN page["+title.getName()+"]");
		
		return navigateTo( title.toString(), null );
	}
	public Response navigateTo(UrlFields uf) {
		if (null == uf)
			throw new RuntimeException(this.getClass()+".navigateTo: uf can't NULL");
		
		Logger.logDebug(this.getClass()+".navigateTo: BEGIN page["+uf.page+"] query["+uf.query+"]");
		
		return navigateTo( uf.page, uf.query );
	}
	/**
	 * MAIN API
	 */
	public Response navigateTo(String requestedPage, String query) {
		
		if (null == requestedPage)
			throw new RuntimeException(this.getClass()+".navigateTo: page can't NULL");
		
		Logger.logDebug(this.getClass()+".navigateTo: BEGIN requestedPage["+requestedPage+"] query["+query+"]");
		
		FlowManager.Response fr = null;
		Exception e = null;
		
		// try fetching the page
		// If we get into trouble:
		//  - Message to user
		//  - navigate to 'default'
		try {
			fr = flowManager.getPage( requestedPage );
		} catch(TitleException te) {
			e = te;
		} catch(NamespaceException ne) {
			e = ne;
		}
		if (null!=e) {
			return handleException( e, requestedPage, query );
		}
		Logger.logDebug(this.getClass()+".navigateTo: cc="+fr.cc+", view="+fr.vp.getViewName());

		// grab the view name... just for the response
		String targetView = fr.vp.getViewName();
		
		// pass along the query parameters
		fr.vp.setRequestedPageParams(requestedPage, query);

		// the page name is different from 
		// the view name AND it is dynamically set (sometimes)
		// once the 'request' parameters are passed along the 'view' object
		String pageName = fr.vp.getPageName();

		// OK, we seem to have a valid page from hereon
		// Check if we are already on this page
		Response rep = null;
		
		if (basePage.isCurrentView(targetView)) {
			Logger.logDebug(this.getClass()+".navigateTo: SAME PAGE");
			//fr.vp.doView();
			//updateHistory( pageName );
			//updateWindowTitle( pageName );
			rep = samePageResponse;
		} else {
			// we really need to navigate it seems...
			fr.vp.setStatusMessageServer(basePage.getStatusMessageServer());
			fr.vp.setUserMessageServer(basePage.getUserMessageServer());
			basePage.onBeforeChangingBody();
			basePage.setBody( fr.vp );
		}
		fr.vp.doView();
		
		// update the history
		// update the window's title
		updateHistory( pageName );
		updateWindowTitle( pageName );

		if (null==rep) {
			rep = new PortalManager.Response();
			rep.code = ResponseCode.OK;
			rep.page = fr.vp.getPageName();
			rep.view = fr.vp.getViewName();
		}
		
		// for IE....
		AnchorsUtil.updateAnchors();
		//Logger.logDebug(this.getClass()+".navigateTo: END page["+rep.page+"] code["+rep.code+"]");		
		
		return rep;
	}
	protected void updateHistory(String page) {
		//History.newItem( page );
	}
	protected void updateHistory( UrlFields target ) {
		//History.newItem( target.toString() );
	}
	protected void updateWindowTitle( UrlFields target ) {
		Window.setTitle( messageManager.site() + "-" + target.page );
	}
	protected void updateWindowTitle( String page ) {
		Window.setTitle( messageManager.site() + "-" + page );
	}

	/*****************************************
	 * HELPERS
	 *****************************************/
	protected UrlFields getDefaultPage() {
		if ( null != defaultPage ) {
			return defaultPage;
		}
		
		if (null == flowManager)
			throw new RuntimeException(this.getClass()+".getDefaultPage: FlowManager not configured");
		
		FlowManager.Response fr = null;
		Exception ex = null;
		try {
			fr = flowManager.getPage("Main");
		} catch (TitleException e) {
			ex = e;
		} catch (NamespaceException e) {
			ex = e;
		}
		if (null!=ex || null==fr)
			throw new RuntimeException(this.getClass()+".getDefaultPage: FlowManager is not configured with 'Main' page");
	
		UrlFields uf = (UrlFields) GWT.create(UrlFields.class);
		
		uf.page = fr.getContentPage().getPageName();
		
		defaultPage = uf;
		
		return defaultPage;
	}
	private Response handleException(Exception e, String page, String query) {
		Logger.logDebug(this.getClass()+".handleException");
		
		String msg = messageManager.invalid_page();
		userMessageManager.sendMessage(msg);
		
		String template = this.getClass() + ".handleException: requestedPage[$page] msg[$msg]"; 
		HashMap<String,String> params = new HashMap<String,String>();
		params.put( "$page", page );
		params.put( "$msg",  e.getMessage() );
		Logger.logInfo( stringUtil.substitute(template, params) );
		
		DeferredCommand.addCommand( new NavigateCommand( this, defaultPage) );
		
		return invalidPageResponse;
	}
	/*****************************************
	 * HISTORY interface
	 *****************************************/

	public void onHistoryChanged(String historyToken) {
		Logger.logDebug(this.getClass()+".onHistoryChanged");
		DeferredCommand.addCommand( new NavigateCommand( this, historyToken) );	
	}
	

}//END